#region License
/*
 * Copyright (c) 2007, Matthew Holmes
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice, this
 * list of conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.
 *
 * * Neither the name of The Mango Project nor the names of its contributors may be
 * used to endorse or promote products derived from this software without specific
 * prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;
using SysMath = System.Math;

using Mango.Framework;
using Mango.Framework.Tree;

using Mogre;

namespace Mango.Framework.Common {
    public class ProjectAsyncResult : IAsyncResult {
        #region Fields

        private object m_State = null;
        private AsyncCallback m_Callback = null;
        private Mutex m_WaitHandle = new Mutex ();
        private string m_Path = String.Empty;
        private bool m_Complete = false;

        #endregion

        #region Constructors

        internal ProjectAsyncResult (string path, AsyncCallback cb, object state) {
            m_Path = path;
            m_Callback = cb;
            m_State = state;
        }

        #endregion

        #region Properties

        internal string Path {
            get {
                return m_Path;
            }
            set {
                m_Path = value;
            }
        }

        internal AsyncCallback Callback {
            get {
                return m_Callback;
            }
            set {
                m_Callback = value;
            }
        }

        #endregion

        #region IAsyncResult Members

        public object AsyncState {
            get {
                return m_State;
            }
            internal set {
                m_State = value;
            }
        }

        public WaitHandle AsyncWaitHandle {
            get {
                return m_WaitHandle;
            }
        }

        public bool CompletedSynchronously {
            get {
                return false;
            }
        }

        public bool IsCompleted {
            get {
                return m_Complete;
            }
            internal set {
                m_Complete = value;
            }
        }

        #endregion
    };

    public enum ResourceLocationType {
        FileSystem = 0,
        Zip = 1
    }

    public class Project {
        #region Events

        public event EventHandler<EventArgs> ProjectLoading;
        public event EventHandler<EventArgs> ProjectLoaded;

        #endregion

        #region Fields

        //public const string ResourceGroupName = "WorkspaceResourceGroup";

        private string m_Path = String.Empty;
        private string m_Name = "NewProject";
        private MonitoredList<KeyValuePair<string, ResourceLocationType>> m_ResourceLocations = new MonitoredList<KeyValuePair<string, ResourceLocationType>> ();
        private List<string> m_MaterialScripts = new List<string> ();
        private List<string> m_CompositorScripts = new List<string> ();
        private ProjectNode m_ProjectRoot = null;
        private bool m_IsDirty = false;
        
        /*private bool m_GroupInit = false;
        private string m_MaterialScript = null;
        private List<string> m_MaterialNames = new List<string> ();
        private Dictionary<string, Material> m_Materials = new Dictionary<string, Material> ();

        private Regex m_MaterialRegex = new Regex (@"material\s+([\w|\/|\\|_]+)\s*\{", RegexOptions.Compiled);*/

        #endregion

        #region Constructor

        public Project() {
            m_ResourceLocations.ListChanged += new EventHandler<EventArgs> (m_ResourceLocations_ListChanged);
            SetupProject ();
        }

        #endregion

        #region Properties

        public string Path {
            get {
                return m_Path;
            }
        }

        public string Name {
            get {
                return m_Name;
            }
            set {
                m_Name = value;
                m_IsDirty = true;
            }
        }

        public MonitoredList<KeyValuePair<string, ResourceLocationType>> ResourceLocations {
            get {
                return m_ResourceLocations;
            }
        }

        public ProjectNode ProjectRoot {
            get {
                return m_ProjectRoot;
            }
        }

        public bool IsDirty {
            get {
                return m_IsDirty;
            }
        }

        #endregion

        #region Public Methods

        public IAsyncResult BeginLoad (string path, AsyncCallback cb, object state) {
            ProjectAsyncResult ar = new ProjectAsyncResult (path, cb, state);
            ThreadPool.QueueUserWorkItem (new WaitCallback (DoThreadedLoad), ar);
            return ar;
        }

        public void EndLoad (IAsyncResult ar) {
            ProjectAsyncResult war = ar as ProjectAsyncResult;
            if (war == null) {
                return;
            }

            Load (war.Path);
        }

        public void Load (string path) {
            try {
                Schema.Project p = LoadXmlProjectFile (path);

                SetupProject ();
                lock(this) {
                    m_Path = path;
                }
            }
            catch {

            }
            /*try {
                SetupResourceGroup ();
                m_Path = path;
                using (StreamReader sr = new StreamReader (path)) {
                    m_MaterialScript = sr.ReadToEnd ();
                }

                ResourceGroupManager.Singleton.ClearResourceGroup (ResourceGroupName);
                m_MaterialNames.Clear ();

                MatchCollection matches = m_MaterialRegex.Matches (m_MaterialScript);
                foreach (Match m in matches) {
                    if (m.Groups.Count > 1) {
                        string name = m.Groups[1].Value;
                        m_MaterialNames.Add (name);
                    }
                }

                using (MemoryStream ms = new MemoryStream (Encoding.ASCII.GetBytes (m_MaterialScript))) {
                    MaterialManager.Singleton.ParseScript (new DataStreamPtr (new ManagedDataStream (ms)), ResourceGroupName);
                }

                foreach (string name in m_MaterialNames) {
                    ResourcePtr res = MaterialManager.Singleton.GetByName (name);
                    if (res == null || res.Group != ResourceGroupName || !(res.Target is Material)) {
                        continue;
                    }

                    ((Material)res.Target).Compile (true);
                    res.Load ();
                    m_Materials.Add (res.Name, (Material)res.Target);
                }

                m_Root = new RootNode (this);
            }
            catch {
                throw;
            }*/
        }

        #endregion

        #region Private Methods

        private void SetupResourceGroup () {
            /*if (!m_GroupInit) {
                ResourceGroupManager.Singleton.CreateResourceGroup (ResourceGroupName);
                ResourceGroupManager.Singleton.InitialiseResourceGroup (ResourceGroupName);
                m_GroupInit = true;
            }*/
        }

        private Schema.Project LoadXmlProjectFile(string path) {
            XmlSerializer serializer = new XmlSerializer (typeof (Schema.Project));
            Schema.Project p = (Schema.Project)serializer.Deserialize (new StreamReader (path));
            if (p.Resources != null) {
                foreach (Schema.ResourceLocation rl in p.Resources.ResourceLocation) {
                    m_ResourceLocations.Add (
                        new KeyValuePair<string, ResourceLocationType> (
                            rl.path,
                            (ResourceLocationType)Enum.Parse (typeof (ResourceLocationType), rl.type.ToString ())
                    ));
                }

                foreach (Schema.MaterialScript ms in p.Resources.MaterialScript) {
                    m_MaterialScripts.Add (ms.path);
                }

                foreach (Schema.CompositorScript cs in p.Resources.CompositorScript) {
                    m_CompositorScripts.Add (cs.path);
                }
            }

            return p;
        }

        private void SetupProject() {
            lock (this) {
                m_IsDirty = false;
                m_ProjectRoot = new ProjectNode (this);
            }
        }

        private void OnProjectLoading () {
            if (ProjectLoading != null) {
                ProjectLoading (this, new EventArgs ());
            }
        }

        private void OnProjectLoaded () {
            if (ProjectLoaded != null) {
                ProjectLoaded (this, new EventArgs ());
            }
        }

        private void DoThreadedLoad (object state) {
            ProjectAsyncResult ar = state as ProjectAsyncResult;
            ar.Callback.Invoke (ar);
        }

        private void m_ResourceLocations_ListChanged (object sender, EventArgs e) {
            m_IsDirty = true;
        }

        #endregion
    }
}
